home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Technotools
/
Technotools (Chestnut CD-ROM)(1993).ISO
/
lang_c
/
cserial
/
8250xon.c
< prev
next >
Wrap
C/C++ Source or Header
|
1990-04-04
|
13KB
|
494 lines
/*
* 8250XON.C
*
* NSC8250 RS232 Xon/Xoff ISR Routine
*
* Written for the
*
* Datalight
* Microsoft V 5.x
* TurboC
* &
* Zortech
*
* C Compilers
*
* Copyright (c) John Birchfield 1987, 1988, 1989
*/
#include <stdio.h>
#include "dependnt.h"
#include "delay.h"
#include "8250nsc.h"
#include "8250xon.h"
#include "queue.h"
#include "timer.h"
#if (!defined (TRUE))
# define TRUE (1)
# define FALSE (0)
#endif
#define I_BUF_SIZE 4096 /* inbyteut Buffer Size */
#define O_BUF_SIZE 4096 /* output Buffer Size */
volatile unsigned XON_PORT_address = 0x03F8;
volatile char xoff_enabled = FALSE,
xoff_sent = FALSE,
xoff_received = FALSE,
XON_PORT_status = 0,
XON_PORT_state = 0,
XON_PORT_command = 0,
XON_char = 0x11,
XOFF_char = 0x13;
char XON_PORT_channel = 1,
SAVE_int_mask = 0, /* saved interrupt controller mask word */
/*
* 8250 register save locations and base address offsets
*/
IER_save = 0, LCR_save = 0, MCR_save = 0, DL_lsb = 0, DL_msb = 0;
QUEUE *xon8250_inqueue, *xon8250_outqueue;
/*
* XON8250_ISR - This is the interrupt handler for the
* National Semiconducter 8250 Serial Chip.
* After installation by Catch_Rt, it catches the
* 8250 interrupts and en_queues incoming characters
* from the Serial Port - and de-queues outgoing
* characters to the Serial Port. The original code
* was written in assembler and provided about 80%
* of the Port's Bandwidth at 9600 baud running
* An Xmodem protocol. We'll see what this does...
*/
#if (!defined (DLC))
void (interrupt far * xon_save_vec) (void);
void interrupt far
xon8250_isr (void)
#else
int
xon8250_isr ()
#endif
{
int ch;
char test_status;
enable ();
test_status = inbyte ((XON_PORT_address + IIR));
do
{
switch (test_status)
{
case IIR_rls:
XON_PORT_status |= inbyte ((XON_PORT_address + LSR));
break;
case IIR_receive:
ch = inbyte (XON_PORT_address);
if ((xoff_enabled && !xoff_sent) &&
(ch == XOFF_char))
{
xoff_received = TRUE;
}
else
if (xoff_received &&
(ch == XON_char))
{
xoff_received = FALSE;
outbyte (XON_PORT_address + IER, XON_PORT_command = RX_TX_enable);
}
else
if ((en_queue (xon8250_inqueue, ch) < 10) &&
xoff_enabled && !xoff_sent && !xoff_received)
{
xoff_sent = TRUE;
while ((inbyte ((XON_PORT_address + LSR)) & 0x20) == 0)
;
outbyte (XON_PORT_address + IER, XON_PORT_command = RX_TX_enable);
outbyte (XON_PORT_address, XOFF_char);
}
break;
case IIR_transmit:
if (xoff_sent && (queue_avail (xon8250_inqueue) > 20))
{
xoff_sent = FALSE;
outbyte (XON_PORT_address, XON_char);
}
else
if (xoff_received)
outbyte (XON_PORT_address + IER, XON_PORT_command = RX_enable);
else
if ((ch = de_queue (xon8250_outqueue)) != -1)
{
outbyte (XON_PORT_address, ch);
}
else
{
outbyte (XON_PORT_address + IER, XON_PORT_command = RX_enable);
}
break;
case IIR_mstatus:
test_status = inbyte ((XON_PORT_address + MSR));
break;
}
} while ((test_status = inbyte (XON_PORT_address + IIR)) != IIR_complete);
disable ();
outbyte (INT_cntrl, EOI_word);
#if (defined (DLC))
return (1);
#endif
}
/*
* XON8250_INIT - Here we get the address of the 8250 Port
* which corresponds to the channel passed in.
* We then massage the 8259 Interrupt Controller
* calculate the Physical Interrupt and save off
* the 8250's current contents. Then attach the
* xon8250_isr routine to the interrupt and
* return the rt returned index for saving - it's
* needed to terminate the interrupt.
*/
#define XON8250_STACK_SIZE 512
int xon8250_intno;
static int xon_intmask [] = { 0xef, 0xf7, 0xef, 0xf7 };
/*
* The above 8259 mask bits are determined from the formula
* mask = ~(1 << (5 - PORT_CHANNEL));
* The array assumes that COM3 and COM4 use the same interrupts
* as COM1 and COM2.
*/
static int xon_intno [] = { 12, 11, 12, 11 };
/*
* The above interrupt number array is based on the algorithm
* xon8250_intno = (13 - PORT_channel);
*/
int
xon8250_init (int channel, int buffer_size)
{
int Dos_address, mask;
XON_PORT_channel = channel;
xon8250_inqueue = alloc_queue (buffer_size);
xon8250_outqueue = alloc_queue (buffer_size);
Dos_address = (XON_PORT_channel - 1) * 2;
peekmem (0x40, Dos_address, XON_PORT_address);
mask = xon_intmask [XON_PORT_channel-1];
SAVE_int_mask = inbyte (INT_mask);
mask &= SAVE_int_mask;
xon8250_intno = xon_intno [XON_PORT_channel-1];
LCR_save = inbyte (XON_PORT_address + LCR);
disable ();
outbyte (XON_PORT_address + LCR, LCR_save | LCR_DLAB);
MCR_save = inbyte (XON_PORT_address + MCR);
DL_lsb = inbyte (XON_PORT_address);
DL_msb = inbyte (XON_PORT_address + 1);
outbyte (XON_PORT_address + LCR, LCR_save & 0x7F);
IER_save = inbyte (XON_PORT_address + IER);
enable ();
#if (defined (DLC))
int_intercept (xon8250_intno, &xon8250_isr, XON8250_STACK_SIZE);
#else
xon_save_vec = getvect (xon8250_intno);
setvect (xon8250_intno, xon8250_isr);
#endif
DELAY_init ();
outbyte (INT_mask, mask);
}
/*
* XON8250_TERM - This routine restores the rs232xon 8250 back to its
* state before xon8250_INIT was called and releases the
* corresponding interrupt back to the system.
*/
void
xon8250_term (int restore)
{
disable ();
outbyte (INT_mask, SAVE_int_mask);
if (restore)
{
outbyte (XON_PORT_address + LCR, LCR_DLAB);
outbyte (XON_PORT_address, DL_lsb);
outbyte (XON_PORT_address + 1, DL_msb);
outbyte (XON_PORT_address + MCR, MCR_save);
outbyte (XON_PORT_address + LCR, 0x7F);
outbyte (XON_PORT_address + IER, IER_save);
outbyte (XON_PORT_address + LCR, LCR_save);
}
#if (defined (DLC))
int_restore (xon8250_intno);
#else
setvect (xon8250_intno, xon_save_vec);
#endif
}
/*
* XON8250_READ - this routine looks in the xon8250_inqueue for a character
*/
int
xon8250_read (void)
{
int ch;
disable ();
ch = de_queue (xon8250_inqueue);
enable ();
if ((XON_PORT_command == RX_enable) &&
((!queue_empty (xon8250_outqueue)) || xoff_sent))
outbyte (XON_PORT_address + IER, XON_PORT_command = RX_TX_enable);
return (ch);
}
/*
* XON8250_TIMED_READ - attempts to read rs232 port - if no char
* available in number of seconds passed
* returns -1
*/
int
xon8250_timed_read (int sec)
{
int ch;
timer_set ();
while ((ch = xon8250_read ()) == -1)
if ((timer_read () / 18) > sec)
break;
return (ch);
}
/*
* XON8250_WRITE - plain vanilla write to the port - check to see that
* the chip may need a kick in the pants before returning
*/
int
xon8250_write (char ch)
{
int rval = -1;
disable ();
rval = en_queue (xon8250_outqueue, ch);
enable ();
if (XON_PORT_command != RX_TX_enable)
outbyte (XON_PORT_address + IER, XON_PORT_command = RX_TX_enable);
return (rval);
}
/*
* XON8250_DTRNR - drop Data Terminal Ready Line
*/
void
xon8250_dtnr (void)
{
char mcr_save;
disable ();
mcr_save = inbyte (XON_PORT_address + MCR);
outbyte (XON_PORT_address + MCR, 0);
DELAY_loop (500);
outbyte (XON_PORT_address + MCR, mcr_save);
enable ();
}
/*
* XON8250_GET_STATUS - returns the current rs232xon status and
* resets any error condition.
*/
int
xon8250_get_status (void)
{
char rval = XON_PORT_status;
XON_PORT_status &= ERROR_reset;
return ((int) rval);
}
/*
* XON8250_XOFF_SENT - did we send an Xoff char?
*/
int
xon8250_xoff_sent (void)
{
return (xoff_sent);
}
/*
* XON8250_WRITE_BUFFER_EMPTY
*/
xon8250_write_buffer_empty (void)
{
return (queue_empty (xon8250_outqueue));
}
/*
* IOCTL_SET_XOFF
* sets the rs232 xon/xoff protocol. It accepts a command string
* of the form
* "y n" or "n n" or "n y" or "y y" either upper or
* lower case.
* The 1st char enables or disables xon/xoff receive...
* the 2d char enables or disables xon/xoff transmit
*/
int
ioctl_set_xoff (char *cmd)
{
char temp;
char xrcv[2];
sscanf (cmd, "%1s", xrcv);
disable ();
xoff_enabled = (toupper (*xrcv) == 'Y') ? TRUE : FALSE;
enable ();
}
/*
* XON8250_WRITE_BREAK - Write a BREAK Character
*/
void
xon8250_write_break (void)
{
int i;
disable ();
while ((inbyte (XON_PORT_address + LSR) & 0x40) == 0)
;
outbyte (XON_PORT_address + LCR, inbyte (XON_PORT_address + LCR) | 0x40);
DELAY_loop (500);
outbyte (XON_PORT_address + LCR, inbyte (XON_PORT_address + LCR) & 0xBF);
enable ();
}
/*
* XON8250_XON_PORT_INIT (Cmd) configures the 8250
* cmd is a string of the form baud parity stop data xon/xoff... i.e.
* 300 n 1 8 y
*
* baud - 300, 600, 1200, 2400, 4800, 9600, 19200
* parity - n -> no parity check
* o -> odd parity
* e -> even parity
* stop - 1 -> 1 stop bit
* 2 -> 2 stop bits
* data - 5, 6, 7, 8 data bits
*/
int
xon8250_port_init (char *cmd)
{
unsigned baud, data, mode_word, parity, stop, xoff;
char pty[2];
sscanf (cmd, "%d %1s %d %d", &baud, pty, &stop, &data);
*pty = toupper (*pty);
switch (*pty)
{
case 'E':
parity = 1;
break;
case 'O':
parity = 3;
break;
case 'N':
parity = 0;
break;
default:
parity = 0;
break;
}
stop = (--stop & 1);
stop <<= 2;
baud /= 10;
baud = 11520 / baud;
parity <<= 3;
parity &= 0x018;
data -= 5;
data &= 3;
mode_word = data | stop | parity;
disable ();
xoff_received = FALSE;
outbyte (XON_PORT_address + LCR, inbyte (XON_PORT_address + LCR) | LCR_DLAB);
outbyte (XON_PORT_address, baud % 256);
outbyte (XON_PORT_address + 1, baud / 256);
outbyte (XON_PORT_address + LCR, mode_word & 0x7F);
outbyte (XON_PORT_address + IER, XON_PORT_command = RX_enable);
outbyte (XON_PORT_address + MCR, 0x0F);
inbyte (XON_PORT_address + LSR);
inbyte (XON_PORT_address + MSR);
inbyte (XON_PORT_address);
enable ();
}
/*---------------------- xon8250_port_enable () ----------------------*/
/*
*
*/
void
xon8250_port_enable (void)
{
disable ();
outbyte (XON_PORT_address + IER, XON_PORT_command = RX_enable);
outbyte (XON_PORT_address + MCR, 0x0F);
inbyte (XON_PORT_address + LSR);
inbyte (XON_PORT_address + MSR);
inbyte (XON_PORT_address);
enable ();
}